package com.yc.sole.erp.biz;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.google.common.collect.Lists;
import com.yc.sole.common.constant.PubConstant;
import com.yc.sole.common.domain.StateCountResp;
import com.yc.sole.erp.dto.*;
import com.yc.sole.erp.enums.ShipmentType;
import com.yc.sole.erp.exception.ErpResultEnum;
import com.yc.sole.erp.repository.*;
import com.yc.sole.erp.request.ShipmentOrderReq;
import com.yc.sole.erp.request.ShipmentQueryReq;
import com.yc.sole.erp.request.ShipmentReq;
import com.yc.sole.erp.response.SaleOrderResp;
import com.yc.sole.erp.response.ShipmentOrderResp;
import com.yc.sole.erp.response.ShipmentResp;
import com.yc.sole.framework.exception.BaseException;
import com.yc.sole.framework.mybatis.page.Paging;
import com.yc.sole.framework.mybatis.util.PageUtil;
import com.yc.sole.system.service.SequenceService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 服务接口实现
 *
 * @author yizuomin
 * @date 2024-01-16
 **/
@Slf4j
@Service
@RequiredArgsConstructor
public class ShipmentBiz {

    private final ShipmentRepository shipmentRepository;
    private final CustomerRepository customerRepository;
    private final SequenceService sequenceService;
    private final SaleOrderRepository saleOrderRepository;
    private final ShipmentOrderRepository shipmentOrderRepository;
    private final ProductRepository productRepository;

    /**
     * 根据id查询
     *
     * @param id
     * @return
     */
    public ShipmentResp selectShipmentById(Long id) {
        ShipmentResp shipmentResp = BeanUtil.copyProperties(shipmentRepository.selectShipmentById(id), ShipmentResp.class);
        if (Objects.isNull(shipmentResp)) {
            return null;
        }
        // 查询送货单详情
        List<ShipmentOrderDto> shipmentOrderDtoList = shipmentOrderRepository.selectByShipmentId(id);
        List<ShipmentOrderResp> orderList = BeanUtil.copyToList(shipmentOrderDtoList, ShipmentOrderResp.class);
        Set<Long> orderIds = shipmentOrderDtoList.stream().map(ShipmentOrderDto::getOrderId).collect(Collectors.toSet());
        Map<Long, SaleOrderDto> saleOrderDtoMap = saleOrderRepository.selectSaleOrderByIds(orderIds).stream().collect(Collectors.toMap(SaleOrderDto::getId, v -> v));
        for (ShipmentOrderResp shipmentOrderResp : orderList) {
            SaleOrderDto saleOrderDto = saleOrderDtoMap.get(shipmentOrderResp.getOrderId());
            if (Objects.nonNull(saleOrderDto)) {
                shipmentOrderResp.setOrderInfo(BeanUtil.copyProperties(saleOrderDto, SaleOrderResp.class));
            }
        }
        shipmentResp.setOrderList(orderList);
        return shipmentResp;
    }

    /**
     * 新增
     *
     * @param shipmentReq
     */
    @Transactional(rollbackFor = Exception.class)
    public void saveShipment(ShipmentReq shipmentReq) {
        // 查询订单
        Set<Long> orderIds = shipmentReq.getOrderList().stream().map(ShipmentOrderReq::getOrderId).collect(Collectors.toSet());
        List<SaleOrderDto> saleOrderDtoList = saleOrderRepository.selectSaleOrderByIds(orderIds);
        if (CollUtil.isEmpty(saleOrderDtoList)) {
            throw new BaseException(ErpResultEnum.SALE_ORDER_NOT_FOUND);
        }
        Set<Long> productIds = saleOrderDtoList.stream().map(SaleOrderDto::getProductId).collect(Collectors.toSet());
        Map<Long, ProductDto> productDtoMap = productRepository.selectProductByIds(productIds).stream().collect(Collectors.toMap(ProductDto::getId, v -> v));
        Map<Long, SaleOrderDto> saleOrderDtoMap = saleOrderDtoList.stream().collect(Collectors.toMap(SaleOrderDto::getId, v -> v));
        // 只能操作同一个客户的订单
        Set<Long> customerIds = saleOrderDtoList.stream().map(SaleOrderDto::getCustomerId).collect(Collectors.toSet());
        if (CollUtil.size(customerIds) > 1) {
            throw new BaseException(ErpResultEnum.ONLY_ALLOW_SAME_CUSTOMER);
        }
        // 查询客户
        CustomerDto customerDto = customerRepository.selectCustomerById(new ArrayList<>(customerIds).get(0));
        if (Objects.isNull(customerDto)) {
            throw new BaseException(ErpResultEnum.CUSTOMER_NOT_FOUND);
        }
        // 记录送货信息
        ShipmentDto dto = new ShipmentDto();
        String generateSeqNo = sequenceService.generateSeqNo(PubConstant.SHIPMENT_SEQ_KEY);
        dto.setShipmentNo(generateSeqNo);
        dto.setShipmentDate(shipmentReq.getShipmentDate());
        Integer totalQuantity = shipmentReq.getOrderList().stream().map(ShipmentOrderReq::getQuantity).reduce(0, Integer::sum);
        dto.setQuantity(totalQuantity);
        dto.setCustomerId(customerDto.getId());
        dto.setCustomerName(customerDto.getCustomerName());
        dto.setShipmentType(shipmentReq.getShipmentType());
        dto.setCarrier(shipmentReq.getCarrier());
        dto.setTrackingNo(shipmentReq.getTrackingNo());
        dto.setShipCost(shipmentReq.getShipCost());
        dto.setRecipientName(shipmentReq.getRecipientName());
        dto.setRecipientPhoneNum(shipmentReq.getRecipientPhoneNum());
        dto.setRecipientAddress(shipmentReq.getRecipientAddress());
        BigDecimal totalAmount = shipmentReq.getOrderList().stream().map(e -> {
            SaleOrderDto saleOrderDto = saleOrderDtoMap.get(e.getOrderId());
            return new BigDecimal(String.valueOf(e.getQuantity())).multiply(saleOrderDto.getUnitPrice());
        }).reduce(BigDecimal.ZERO, BigDecimal::add);
        dto.setAmount(totalAmount);
        dto.setRemark(shipmentReq.getRemark());
        Long shipmentId = shipmentRepository.saveShipment(dto);

        // 记录送货详情
        Set<ShipmentOrderDto> shipmentOrderDtoList = shipmentReq.getOrderList().stream().map(shipmentOrder -> {
            SaleOrderDto saleOrderDto = saleOrderDtoMap.get(shipmentOrder.getOrderId());
            ProductDto productDto = productDtoMap.get(saleOrderDto.getProductId());
            ShipmentOrderDto shipmentOrderDto = new ShipmentOrderDto();
            shipmentOrderDto.setShipmentId(shipmentId);
            shipmentOrderDto.setOrderId(saleOrderDto.getId());
            BigDecimal amount = new BigDecimal(String.valueOf(shipmentOrder.getQuantity())).multiply(saleOrderDto.getUnitPrice());
            shipmentOrderDto.setAmount(amount);
            shipmentOrderDto.setQuantity(shipmentOrder.getQuantity());
            shipmentOrderDto.setRemark(shipmentOrder.getRemark());
            // 增加订单已送货数量
            saleOrderRepository.updateShippedQuantity(shipmentOrder.getOrderId(), shipmentOrder.getQuantity());
            // 扣减产品库存
            productRepository.updateStock(productDto.getId(), -shipmentOrder.getQuantity(), shipmentReq.getShipmentDate() + "送货单");
            return shipmentOrderDto;
        }).collect(Collectors.toSet());
        shipmentOrderRepository.saveShipmentOrder(shipmentOrderDtoList);
    }

    /**
     * 修改
     *
     * @param shipmentReq
     */
    @Transactional(rollbackFor = Exception.class)
    public void updateShipmentById(ShipmentReq shipmentReq) {
        // 查询送货单
        ShipmentDto shipmentDto = shipmentRepository.selectShipmentById(shipmentReq.getId());
        if (Objects.isNull(shipmentDto)) {
            throw new BaseException(ErpResultEnum.SHIPMENT_NOT_FOUND);
        }
        // 查询订单
        Set<Long> newOrderIds = shipmentReq.getOrderList().stream().map(ShipmentOrderReq::getOrderId).collect(Collectors.toSet());
        List<SaleOrderDto> newSaleOrderDtoList = saleOrderRepository.selectSaleOrderByIds(newOrderIds);
        Set<Long> productIds = newSaleOrderDtoList.stream().map(SaleOrderDto::getProductId).collect(Collectors.toSet());
        Map<Long, ProductDto> newProductDtoMap = productRepository.selectProductByIds(productIds).stream().collect(Collectors.toMap(ProductDto::getId, v -> v));
        if (CollUtil.isEmpty(newSaleOrderDtoList)) {
            throw new BaseException(ErpResultEnum.SALE_ORDER_NOT_FOUND);
        }
        Map<Long, SaleOrderDto> newSaleOrderDtoMap = newSaleOrderDtoList.stream().collect(Collectors.toMap(SaleOrderDto::getId, v -> v));
        // 只能操作同一个客户的订单
        Set<Long> customerIds = newSaleOrderDtoList.stream().map(SaleOrderDto::getCustomerId).collect(Collectors.toSet());
        if (CollUtil.size(customerIds) > 1) {
            throw new BaseException(ErpResultEnum.ONLY_ALLOW_SAME_CUSTOMER);
        }
        // 查询客户
        CustomerDto customerDto = customerRepository.selectCustomerById(new ArrayList<>(customerIds).get(0));
        if (Objects.isNull(customerDto)) {
            throw new BaseException(ErpResultEnum.CUSTOMER_NOT_FOUND);
        }
        // 修改送货单
        Integer totalQuantity = shipmentReq.getOrderList().stream().map(ShipmentOrderReq::getQuantity).reduce(0, Integer::sum);
        ShipmentDto updateDto = new ShipmentDto();
        updateDto.setId(shipmentDto.getId());
        updateDto.setShipmentNo(shipmentDto.getShipmentNo());
        updateDto.setShipmentDate(shipmentReq.getShipmentDate());
        updateDto.setQuantity(totalQuantity);
        updateDto.setCustomerId(customerDto.getId());
        updateDto.setCustomerName(customerDto.getCustomerName());
        updateDto.setShipmentType(shipmentReq.getShipmentType());
        updateDto.setCarrier(shipmentReq.getCarrier());
        updateDto.setTrackingNo(shipmentReq.getTrackingNo());
        updateDto.setShipCost(shipmentReq.getShipCost());
        updateDto.setRecipientName(shipmentReq.getRecipientName());
        updateDto.setRecipientPhoneNum(shipmentReq.getRecipientPhoneNum());
        updateDto.setRecipientAddress(shipmentReq.getRecipientAddress());
        BigDecimal totalAmount = shipmentReq.getOrderList().stream().map(e -> {
            SaleOrderDto saleOrderDto = newSaleOrderDtoMap.get(e.getOrderId());
            return new BigDecimal(String.valueOf(e.getQuantity())).multiply(saleOrderDto.getUnitPrice());
        }).reduce(BigDecimal.ZERO, BigDecimal::add);
        updateDto.setAmount(totalAmount);
        updateDto.setRemark(shipmentReq.getRemark());
        shipmentRepository.updateShipmentById(updateDto);
        // 查询原送货单详情
        List<ShipmentOrderDto> shipmentOrderDtoList = shipmentOrderRepository.selectByShipmentId(shipmentDto.getId());
        Set<Long> oriOrderIds = shipmentOrderDtoList.stream().map(ShipmentOrderDto::getOrderId).collect(Collectors.toSet());
        List<SaleOrderDto> oriSaleOrderDtoList = saleOrderRepository.selectSaleOrderByIds(oriOrderIds);
        Set<Long> oriProductIds = oriSaleOrderDtoList.stream().map(SaleOrderDto::getProductId).collect(Collectors.toSet());
        Map<Long, SaleOrderDto> oriSaleOrderDtoMap = oriSaleOrderDtoList.stream().collect(Collectors.toMap(SaleOrderDto::getId, v -> v));
        Map<Long, ProductDto> oriProductDtoMap = productRepository.selectProductByIds(oriProductIds).stream().collect(Collectors.toMap(ProductDto::getId, v -> v));
        for (ShipmentOrderDto shipmentOrderDto : shipmentOrderDtoList) {
            SaleOrderDto saleOrderDto = oriSaleOrderDtoMap.get(shipmentOrderDto.getOrderId());
            ProductDto productDto = oriProductDtoMap.get(saleOrderDto.getProductId());
            // 减少已发货数量
            saleOrderRepository.updateShippedQuantity(shipmentOrderDto.getOrderId(), -shipmentOrderDto.getQuantity());
            // 回退产品库存
            productRepository.updateStock(productDto.getId(), shipmentOrderDto.getQuantity(), shipmentReq.getShipmentDate() + "修改送货单");
        }
        Set<ShipmentOrderDto> saveShipOrderList = shipmentReq.getOrderList().stream().map(o -> {
            SaleOrderDto saleOrderDto = newSaleOrderDtoMap.get(o.getOrderId());
            ProductDto productDto = newProductDtoMap.get(saleOrderDto.getProductId());
            ShipmentOrderDto shipmentOrderDto = new ShipmentOrderDto();
            shipmentOrderDto.setShipmentId(shipmentDto.getId());
            shipmentOrderDto.setOrderId(saleOrderDto.getId());
            BigDecimal amount = new BigDecimal(String.valueOf(o.getQuantity())).multiply(saleOrderDto.getUnitPrice());
            shipmentOrderDto.setAmount(amount);
            shipmentOrderDto.setQuantity(o.getQuantity());
            shipmentOrderDto.setRemark(o.getRemark());
            // 增加已发货数量
            saleOrderRepository.updateShippedQuantity(shipmentOrderDto.getOrderId(), o.getQuantity());
            // 扣减产品库存
            productRepository.updateStock(productDto.getId(), -o.getQuantity(), shipmentReq.getShipmentDate() + "送货单");
            return shipmentOrderDto;
        }).collect(Collectors.toSet());
        // 删除原送货单
        shipmentOrderRepository.deleteByShipmentId(shipmentDto.getId());
        // 保存新的送货单详情
        shipmentOrderRepository.saveShipmentOrder(saveShipOrderList);
    }

    /**
     * 删除
     *
     * @param ids
     */
    @Transactional(rollbackFor = Exception.class)
    public void deleteShipment(Set<Long> ids) {
        for (Long id : ids) {
            // 查询送货信息
            ShipmentDto shipmentDto = shipmentRepository.selectShipmentById(id);
            if (Objects.isNull(shipmentDto)) {
                throw new BaseException(ErpResultEnum.STOCK_OUT_NOT_FOUND);
            }
            // 查询送货详情
            List<ShipmentOrderDto> shipmentOrderDtoList = shipmentOrderRepository.selectByShipmentId(shipmentDto.getId());
            if (CollUtil.isEmpty(shipmentOrderDtoList)) {
                throw new BaseException(ErpResultEnum.SHIPMENT_ORDER_NOT_FOUND);
            }
            for (ShipmentOrderDto shipmentOrderDto : shipmentOrderDtoList) {
                // 查询订单
                SaleOrderDto saleOrderDto = saleOrderRepository.selectSaleOrderById(shipmentOrderDto.getOrderId());
                if (Objects.isNull(saleOrderDto)) {
                    throw new BaseException(ErpResultEnum.SALE_ORDER_NOT_FOUND);
                }
                ProductDto productDto = productRepository.selectProductById(saleOrderDto.getProductId());
                if (Objects.isNull(productDto)) {
                    throw new BaseException(ErpResultEnum.PRODUCT_NOT_FOUND);
                }
                // 减少订单已送货数量
                saleOrderRepository.updateShippedQuantity(saleOrderDto.getId(), -shipmentDto.getQuantity());
                // 回退产品库存
                productRepository.updateStock(productDto.getId(), shipmentDto.getQuantity(), shipmentDto.getShipmentDate() + "删除送货单");
            }
            // 删除送货单详情
            shipmentOrderRepository.deleteByShipmentId(id);
        }
        // 删除送货单
        shipmentRepository.deleteShipmentByIds(ids);
    }

    /**
     * 分页查询
     *
     * @param page
     * @param queryReq
     * @return
     */
    public Paging<ShipmentResp> selectPageWithOrder(Paging<ShipmentQueryReq> page, ShipmentQueryReq queryReq) {
        Paging<ShipmentResp> shipmentRespPaging = PageUtil.pageCopy(shipmentRepository.selectShipmentPage(page, queryReq), ShipmentResp.class);
        if (CollUtil.isEmpty(shipmentRespPaging.getRecords())) {
            return shipmentRespPaging;
        }
        Set<Long> shipmentIds = shipmentRespPaging.getRecords().stream().map(ShipmentResp::getId).collect(Collectors.toSet());
        Map<Long, List<ShipmentOrderDto>> shipmentOrderMap = shipmentOrderRepository.selectByShipmentIds(shipmentIds).stream().collect(Collectors.groupingBy(ShipmentOrderDto::getShipmentId));
        for (ShipmentResp shipmentResp : shipmentRespPaging.getRecords()) {
            List<ShipmentOrderDto> shipmentOrderDtoList = shipmentOrderMap.get(shipmentResp.getId());
            shipmentResp.setOrderList(BeanUtil.copyToList(shipmentOrderDtoList, ShipmentOrderResp.class));
        }
        return shipmentRespPaging;
    }

    /**
     * 分页查询
     *
     * @param page
     * @param queryReq
     * @return
     */
    public Paging<ShipmentResp> selectShipmentPage(Paging<ShipmentQueryReq> page, ShipmentQueryReq queryReq) {
        return PageUtil.pageCopy(shipmentRepository.selectShipmentPage(page, queryReq), ShipmentResp.class);
    }

    /**
     * 查询送货类型的数量
     *
     * @param queryReq
     * @return
     */
    public List<StateCountResp> selectShipmentTypeCount(ShipmentQueryReq queryReq) {
        List<StateCountResp> countRespList = shipmentRepository.selectShipmentTypeCount(queryReq);
        Long totalCount = countRespList.stream().map(StateCountResp::getCount).reduce(0L, Long::sum);
        ArrayList<StateCountResp> resultList = Lists.newArrayList(new StateCountResp("-1", "全部", totalCount));
        Map<String, StateCountResp> stateCountRespMap = countRespList.stream().collect(Collectors.toMap(StateCountResp::getState, v -> v));
        List<StateCountResp> respList = Arrays.stream(ShipmentType.values()).map(state -> {
            StateCountResp countResp = stateCountRespMap.get(state.getValue());
            if (Objects.isNull(countResp)) {
                countResp = new StateCountResp();
                countResp.setCount(0L);
            }
            countResp.setStateName(state.getDescription());
            countResp.setState(state.getValue());
            return countResp;
        }).toList();
        resultList.addAll(respList);
        return resultList;
    }

    /**
     * 根据订单号查询
     *
     * @param orderId
     * @return
     */
    public List<ShipmentOrderResp> selectShipmentByOrderId(Long orderId) {
        List<ShipmentOrderDto> shipmentDtoList = shipmentOrderRepository.selectByOrderId(orderId);
        Set<Long> shipmentIds = shipmentDtoList.stream().map(ShipmentOrderDto::getShipmentId).collect(Collectors.toSet());
        Set<Long> orderIds = shipmentDtoList.stream().map(ShipmentOrderDto::getOrderId).collect(Collectors.toSet());
        Map<Long, ShipmentDto> shipmentDtoMap = shipmentRepository.selectShipmentByIds(shipmentIds).stream().collect(Collectors.toMap(ShipmentDto::getId, v -> v));
        List<ShipmentOrderResp> orderRespList = BeanUtil.copyToList(shipmentDtoList, ShipmentOrderResp.class);

        Map<Long, SaleOrderDto> saleOrderDtoMap = saleOrderRepository.selectSaleOrderByIds(orderIds).stream().collect(Collectors.toMap(SaleOrderDto::getId, v -> v));
        for (ShipmentOrderResp shipmentOrderResp : orderRespList) {
            ShipmentDto shipmentDto = shipmentDtoMap.get(shipmentOrderResp.getShipmentId());
            if (Objects.isNull(shipmentDto)) {
                continue;
            }
            SaleOrderDto saleOrderDto = saleOrderDtoMap.get(shipmentOrderResp.getOrderId());
            if (Objects.nonNull(saleOrderDto)) {
                shipmentOrderResp.setOrderInfo(BeanUtil.copyProperties(saleOrderDto, SaleOrderResp.class));
            }
        }
        return orderRespList;
    }
}
